#include "deps.h"

#include <iostream>

using namespace std;

Deps::Deps(string file_name) : dependencies_(NULL),
    file_name_(file_name), file_size_(0), completed_deps_counter_(0),
    heuristic_time_(0), exec_done_(false), exec_time_(0) {
      this->Init();
}

Deps::Deps(string file_name, long file_size) : dependencies_(NULL),
    file_name_(file_name), file_size_(file_size), completed_deps_counter_(0),
    heuristic_time_(0), exec_done_(false), exec_time_(0) {
      this->Init();
}

void Deps::Init() {
  pthread_mutex_init(&(this->node_lock_), NULL);
}

bool operator<(Deps &lhs, Deps &rhs) {
  return (lhs.priority() < rhs.priority()) ? true : false;
}

Deps::~Deps() {
  pthread_mutex_destroy(&(this->node_lock_));
}

/**
* Subject calls update on the observers along with the information of the update
* type. The observer takes appropriate actions
*
* @Param update_type
*/
void Deps::Update(UpdateInfo update_info) {
  // Depending on the type of update, action is carried out
  int update_type = update_info.update_type_;
  Deps* deps_from = (Deps *) update_info.from_subj_;
  switch (update_type) {

    case UpdateInfo::COMPLETED:
      // Increment the counter that keeps track of completion of dependent jobs
      pthread_mutex_lock(&node_lock_);
      completed_deps_counter_++;

      // If the total completed jobs = the total dependent
      // jobs, then this becomes the new set of leaves
      if (completed_deps_counter_ == dependencies_.size()) {
        ScheduleTarget();
      }
      pthread_mutex_unlock(&node_lock_);
      break;

    default:
      break;
      
  }
}

/**
* When the target has all its dependencies completed, then send message to the
* observers to schedule this target.
*/
void Deps::ScheduleTarget() {
  list<Observer *>::iterator it;
  // Iterates over the list of observers and calls update with the update_type
  for (it = observers_.begin(); it != observers_.end(); it++) {
    UpdateInfo update_info;
    update_info.update_type_ = UpdateInfo::SCHEDULE;
    update_info.from_subj_ = this;
    (*it)->Update(update_info);
  }
}

/**
* Registers the observer passed in
*
* @Param o observer to be registered
*/
void Deps::RegisterObserver(Observer *o) {
  observers_.push_back(o);
}

/**
* The Scheduler calls this function call to indicate that the execution is
* completed.
*/
void Deps::FinishExecution() {
  exec_done_ = true;
  list<Observer *>::iterator it;
  // Iterates over the list of observers and calls update with the update_type
  for (it = observers_.begin(); it != observers_.end(); it++) {
    UpdateInfo update_info;
    update_info.update_type_ = UpdateInfo::COMPLETED;
    update_info.from_subj_ = this;
    (*it)->Update(update_info);
  }
}

/**
* Removes the observer
*
* @Param o observer to be removed
*/
void Deps::RemoveObserver(Observer *o) {
  list<Observer *>::iterator it;
  // Iterates over the list of observers and removes the matching one
  for (it = observers_.begin(); it != observers_.end(); it++) {
    if (*it == o) {
      observers_.erase(it);
      return;
    }
  }
}

/**
* Notifies all observers
*/
void Deps::NotifyObservers() {
  list<Observer *>::iterator it;
  // Iterates over the list of observers and calls update with the update_type
  for (it = observers_.begin(); it != observers_.end(); it++) {
//    (*it)->Update(update_type_);
  }
}
